package com.dgut.app.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dgut.app.param.MoneyRecordParam;
import com.dgut.app.param.RegisterParam;
import com.dgut.app.result.LoginResult;
import com.dgut.app.result.UserResult;
import com.dgut.app.service.UserService;
import com.dgut.common.config.WxAppConfig;
import com.dgut.common.entity.*;
import com.dgut.common.enums.StatusEnum;
import com.dgut.common.mapper.CollectorApplicationMapper;
import com.dgut.common.mapper.MoneyRecordMapper;
import com.dgut.common.mapper.UserInfoMapper;
import com.dgut.common.mapper.WxUserMapper;
import com.dgut.common.param.UserAuthParam;
import com.dgut.common.utils.ApiException;
import com.dgut.common.utils.ErrorEnum;
import com.dgut.common.utils.JwtUtil;
import com.dgut.common.utils.PageVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

/**
 * @author IT00ZYQ
 * @date 2022/1/5 12:32
 **/
@Service
@Slf4j
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final WxUserMapper userMapper;
    private final UserInfoMapper infoMapper;
    private final MoneyRecordMapper recordMapper;
    private final CollectorApplicationMapper applicationMapper;

    @Override
    public LoginResult register(RegisterParam param) {
        final WxMaUserService appService = WxAppConfig.wxMaService.getUserService();
        try {
            // 1.根据code获取sessionKey等信息
            WxMaJscode2SessionResult sessionInfo = appService.getSessionInfo(param.getCode());
            // 2.判断用户是否已注册
            QueryWrapper<WxUser> wrapper = new QueryWrapper<>();
            wrapper.eq("open_id", sessionInfo.getOpenid());
            if (userMapper.selectOne(wrapper) != null) {
                throw new ApiException(ErrorEnum.USER_REGISTERED);
            }

            // 3.用户信息校验
            if (!appService.checkUserInfo(sessionInfo.getSessionKey(), param.getRawData(), param.getSignature())) {
                throw new ApiException(ErrorEnum.CHECK_ERROR);
            }

            // 4.解密用户信息
            WxMaUserInfo userInfo = appService.getUserInfo(sessionInfo.getSessionKey(), param.getEncryptedData(), param.getIv());

            // 6.用户注册，写入数据库
            WxUser wxUser = WxUser.builder()
                    .nickName(userInfo.getNickName())
                    .avatarUrl(userInfo.getAvatarUrl())
                    .gender(Integer.valueOf(userInfo.getGender()))
                    .openId(sessionInfo.getOpenid())
                    .allMoney(0)
                    .curMoney(0)
                    .deleted(0)
                    .disabled(0)
                    .identity(0)
                    .build();
            userMapper.insert(wxUser);

            // 7. 生成token并返回
            LoginResult result = new LoginResult();
            BeanUtils.copyProperties(wxUser, result);
            result.setToken(JwtUtil.getToken(wxUser.getOpenId()));
            return result;
        } catch (WxErrorException e) {
            log.error("【微信用户注册失败】:{}", e.getMessage());
            throw new ApiException(ErrorEnum.WX_USER_REGISTER_FAIL);
        }
    }

    @Override
    public LoginResult login(String code) {
        try {
            WxMaJscode2SessionResult sessionInfo = WxAppConfig.wxMaService.getUserService().getSessionInfo(code);
            // 查询用户
            QueryWrapper<WxUser> wrapper = new QueryWrapper<>();
            wrapper.eq("open_id", sessionInfo.getOpenid());
            WxUser user = userMapper.selectOne(wrapper);
            if (user == null) {
                throw new ApiException(ErrorEnum.USER_NO_EXIST);
            }
            LoginResult result = new LoginResult();
            BeanUtils.copyProperties(user, result);
            // 生成token并返回
            result.setToken(JwtUtil.getToken(user.getOpenId()));
            return result;
        } catch (WxErrorException e) {
            log.error("【微信用户登录失败】:{}", e.getMessage());
            throw new ApiException(ErrorEnum.WX_USER_LOGIN_FAIL);
        }
    }

    @Override
    public UserResult userInfo(String token) {
        WxUser user = JwtUtil.getUser(token);
        return UserResult.builder()
                .info(infoMapper.selectByUserIdNoState(user.getId()))
                .user(user)
                .build();
    }

    @Override
    public void userAuth(UserAuthParam param, String token) {
        WxUser user = JwtUtil.getUser(token);
        UserInfo info = infoMapper.selectByUserId(user.getId(), StatusEnum.WAIT_AUDIT.getState());
        // 用于标记用户认证信息是否已经存在
        boolean tag = true;
        if (info == null) {
            tag = false;
            info = new UserInfo();
        }
        BeanUtils.copyProperties(param, info);
        info.setAddress(param.getProvince() + param.getCity() + param.getTown() + param.getDetailAddr());
        info.setState(StatusEnum.WAIT_AUDIT.getState());
        info.setSubmitTime(new Date());
        info.setCertificationUrl(param.getCertificationUrl());
        info.setUserId(user.getId());

        if (tag) {
            // 用户认证信息已存在，只需要更新
            infoMapper.updateById(info);
        } else {
            // 用户认证信息不存在，需要插入
            infoMapper.insert(info);
        }

    }

    @Override
    public void collectorAuth(String reason, String token) {
        WxUser user = JwtUtil.getUser(token);
        if (!StatusEnum.USER.getState().equals(user.getIdentity())) {
            throw new ApiException("用户身份不正确");
        }
        applicationMapper.insert(new CollectorApplication()
                .setUserId(user.getId())
                .setReason(reason)
                .setState(StatusEnum.WAIT_AUDIT.getState()));
    }


    @Override
    public CollectorApplication collectorAuthInfo(String token) {
        WxUser user = JwtUtil.getUser(token);
        return applicationMapper.selectByUserId(user.getId());
    }

    @Override
    public PageVO moneyRecord(MoneyRecordParam param, String token) {
        WxUser user = JwtUtil.getUser(token);
        QueryWrapper<MoneyRecord> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", user.getId());
        wrapper.orderByDesc("create_time");
        if (param.getBeginTime() != null && param.getEndTime() != null) {
            wrapper.between("create_time", param.getBeginTime(), param.getEndTime());
        }
        IPage<MoneyRecord> page = new Page<MoneyRecord>().setCurrent(param.getPageNum()).setSize(param.getPageSize());
        IPage<MoneyRecord> recordPage = recordMapper.selectPage(page, wrapper);
        return PageVO.convert(recordPage);
    }

    @Override
    @Transactional
    public void withdraw(Integer amount, String token) {
        WxUser user = JwtUtil.getUser(token);
        if (amount.compareTo(0) <= 0) {
            throw new ApiException("请输入正确金额");
        }
        if (user.getCurMoney().compareTo(amount) < 0) {
            throw new ApiException("余额不足");
        }

        // 添加流水记录
        recordMapper.insert(new MoneyRecord()
                .setAmount(amount)
                .setUserId(user.getId())
                .setType(StatusEnum.WITHDRAW.getState()));

        // 更新用户余额
        userMapper.updateCurMoney(amount, user.getId());
    }


}
